import {StarType, starTypeData, starTypeMap} from "../ts/StarType.ts";
import {Star} from "./Star.ts";
import {Planet} from "./Planet.ts";
import {
    coldPlanetTypeData,
    gasPlanetTypeData,
    hotPlanetTypeData,
    PlanetType,
    planetTypeMap,
    warmPlanetTypeData
} from "./PlanetType.ts";
import {PlanetResource} from "./PlanetResource.ts";
import {planetTypeResourceData} from "../ts/PlanetTypeItem.ts";
import {Util} from "./Util.ts";

export class Config {
    starCount = 64
    starDistanceAddition = 1
    rareAddition = 1
    planetCountAddition = 1
    planetDistanceAddition = 1
    lyToAu = 60
    AuToM = 40000
    planetFirstAuMin = 0.5
    planetFirstAuMax = 1.5
    planetMaxLuminosity = 5
    randomStartStar = false
    randomStartPlanet = false
    starId = 0
    planetId = 0
    planetResourceId = 0
    distanceArray = [1, 2, 3, 5]
}

export let normal = new Config()

export let maga = Object.assign(new Config(), {
    starCount: 256,
    planetCountAddition: 5,
    planetMaxLuminosity: 10,
    lyToAu: 640,
    AuToM: 100000,
    distanceArray: [1, 1.5, 1.9, 2.6, 3.4, 4.5, 5.9, 7.8, 10.3, 13.6, 18, 23.7, 31.3, 41.3, 54.5, 72, 95, 125, 165, 218, 288, 380, 502, 663, 876, 1156]
})

export let build = (config: Config = normal) => {
    let starArray: Array<Star> = []
    let planetArray: Array<Planet> = []
    let planetResourceArray: Array<PlanetResource> = []

    // 生成初始星球
    let starType: StarType = Object.assign({}, starTypeMap.get(3)) || new StarType()
    if (config.randomStartStar) {
        starType = Object.assign({}, starTypeData[Math.floor(Math.random() * starTypeData.length)])
    }

    // 初始恒星
    starArray.push(Object.assign(new Star(), {
        id: ++config.starId,
        name: 'S-' + config.starId,
        luminosity: Util.formatNumber(Math.random() * (starType.luminosityMax - starType.luminosityMin) + starType.luminosityMin),
        typeId: starType.id,
        x: 0,
        y: 0,
        z: 0
    }))

    // 第一热 第二气巨 第三地中海
    let distance = Util.formatNumber(Math.random() * (config.planetFirstAuMax - config.planetFirstAuMin) + config.planetFirstAuMin)
    distance *= config.AuToM

    let planetType = hotPlanetTypeData[Math.floor(Math.random() * hotPlanetTypeData.length)]
    starType.hot -= 1
    generatePlanet(config, planetArray, planetResourceArray, starArray[0], distance, planetType)
    // 2

    planetType = gasPlanetTypeData[Math.floor(Math.random() * gasPlanetTypeData.length)]
    starType.gas -= 1
    generatePlanet(config, planetArray, planetResourceArray, starArray[0], distance, planetType)
    // 3

    planetType = planetTypeMap.get(1) || new PlanetType()
    if (config.randomStartPlanet) {
        planetType = warmPlanetTypeData[Math.floor(Math.random() * warmPlanetTypeData.length)]
    }
    starType.warm -= 1
    generatePlanet(config, planetArray, planetResourceArray, starArray[0], distance, planetType)
    // 4

    planetType = getPlanetType(starType)
    generatePlanet(config, planetArray, planetResourceArray, starArray[0], distance, planetType)

    let array: Array<StarType> = []

    // 其他恒星
    for (let i = starArray.length; i < config.starCount; i++) {
        if (array.length == 0) {
            for (let i = 0; i < starTypeData.length; i++) {
                for (let j = 0; j < starTypeData[i].percent; j++) {
                    array.push(Object.assign({}, starTypeData[i]))
                }
            }
        }
        let starType = array[Math.floor(Math.random() * array.length)]
        array.splice(array.indexOf(starType), 1)
        generateStar(config, starArray, planetArray, planetResourceArray, starType)
    }

    localStorage.setItem('save', JSON.stringify({
        starData: starArray,
        planetData: planetArray,
        planetResourceData: planetResourceArray,
        current: {
            star: starArray[0],
            planet: planetArray[2]
        },
        target: {
            star: starArray[0],
            planet: planetArray[2]
        }
    }))
    window.location.reload()
}

let generateStar = (config: Config, starArray: Array<Star>, planetArray: Array<Planet>, planetResourceArray: Array<PlanetResource>, starType: StarType) => {

    let position = calPosition(config, starArray)
    let star = Object.assign(new Star(), {
        id: ++config.starId,
        name: 'S-' + config.starId,
        luminosity: Util.formatNumber(Math.random() * (starType.luminosityMax - starType.luminosityMin) + starType.luminosityMin),
        typeId: starType.id,
        x: position.x,
        y: position.y,
        z: position.z
    })

    let distance = Util.formatNumber(Math.random() * (config.planetFirstAuMax - config.planetFirstAuMin) + config.planetFirstAuMin)
    distance *= config.AuToM

    // 计算要生成的行星数量
    let planetCount = Math.floor(Math.random() * (starType.planetMax - starType.planetMin) + starType.planetMin)
    for (let i = 0; i < planetCount; i++) {
        generatePlanet(config, planetArray, planetResourceArray, star, distance, getPlanetType(starType), position.resource)
    }

    starArray.push(star)
}

let calPosition = (config: Config, starArray: Array<Star>, scope: number = 10): { x: number, y: number, z: number, resource: number } => {
    let x = Util.formatNumber(Math.random() * scope - scope / 2)
    let y = Util.formatNumber(Math.random() * scope - scope / 2)
    let z = Util.formatNumber(Math.random() * scope - scope / 2)
    for (let star of starArray) {
        let distance = Math.sqrt(Math.pow(star.x - x, 2) + Math.pow(star.y - y, 2) + Math.pow(star.z - z, 2))
        if (distance < 10) {
            return calPosition(config, starArray, ++scope)
        }
    }
    return {
        x: x,
        y: y,
        z: z,
        resource: Util.formatNumber(Math.log(Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2) + Math.pow(z, 2))))
    }
}

let getPlanetType = (starType: StarType) => {
    let all = 0
    if (starType.hot > 0) {
        all += starType.hot
    }
    if (starType.warm > 0) {
        all += starType.warm
    }
    if (starType.cold > 0) {
        all += starType.cold
    }
    if (starType.gas > 0) {
        all += starType.gas
    }
    let ran = Math.random() * all
    if (starType.hot > 0) {
        ran -= starType.hot
        if (ran < 0) {
            starType.hot -= 1
            return hotPlanetTypeData[Math.floor(Math.random() * hotPlanetTypeData.length)]
        }
    }
    if (ran >= 0 && starType.warm > 0) {
        ran -= starType.warm
        if (ran < 0) {
            starType.warm -= 1
            return warmPlanetTypeData[Math.floor(Math.random() * warmPlanetTypeData.length)]
        }
    }
    if (ran >= 0 && starType.cold > 0) {
        ran -= starType.cold
        if (ran < 0) {
            starType.cold -= 1
            return coldPlanetTypeData[Math.floor(Math.random() * coldPlanetTypeData.length)]
        }
    }
    starType.gas -= 1
    return gasPlanetTypeData[Math.floor(Math.random() * gasPlanetTypeData.length)]
}

let generatePlanet = (config: Config, planetArray: Array<Planet>, planetResourceArray: Array<PlanetResource>, star: Star, distance: number, planetType: PlanetType, resource: number = 1) => {

    let planet = Object.assign(new Planet(), {
        id: ++config.planetId,
        starId: star.id,
        name: 'S-' + star.id + 'P-' + config.planetId,
        planetTypeId: planetType.id,
        distance: Util.formatNumber(distance * config.distanceArray[Math.floor(Math.random() * config.distanceArray.length)]),
        wind: planetType.wind,
        luminosity: Util.formatNumber(Math.min(star.luminosity * 10 * config.AuToM / distance, config.planetMaxLuminosity)),
        landPercent: planetType.land,
        oceanId: planetType.oceanId,
        area: 10000
    })

    planetArray.push(planet)

    let planetTypeResourceArray = planetTypeResourceData.filter((value) => {
        return value.planetTypeId === planetType.id
    })
    for (let planetTypeResource of planetTypeResourceArray) {
        let ran = Math.random()
        if (ran > planetTypeResource.percent) {
            continue
        }
        let total = Math.floor(calNum(planetTypeResource.count) * resource)

        planetResourceArray.push(Object.assign(new PlanetResource(), {
            id: ++config.planetResourceId,
            planetId: config.planetId,
            itemId: planetTypeResource.itemId,
            total: total,
            remain: total,
            recover: Util.formatNumber(calNum(planetTypeResource.recover))
        }))
    }
}

let calNum = (num: number) => {
    // 80% 打9折 60% 打8折 40% 7折 20% 6折
    let ran = Math.random()
    if (ran < 0.2) {
        return num * (0.5 + ran)
    } else if (ran < 0.4) {
        return num * (0.6 + ran)
    } else if (ran < 0.6) {
        return num * (0.7 + ran)
    } else if (ran < 0.8) {
        return num * (0.8 + ran)
    } else {
        return num * (0.9 + ran)
    }
}
